"""Shared utilities and constants for Streamlit UI views."""
from __future__ import annotations

from datetime import date, datetime, timedelta
from typing import Dict, Optional

import streamlit as st

from app.utils.db import db_session
from app.utils.logging import get_logger
from app.ui.navigation import navigate_top_menu

LOGGER = get_logger(__name__)
LOG_EXTRA = {"stage": "ui"}

LLM_PROMPT_TOKENS_PER_CALL = 230
LLM_COMPLETION_TOKENS_PER_CALL = 80


def get_query_params() -> dict[str, list[str]]:
    """Safely read URL query parameters from Streamlit."""
    try:
        return dict(st.query_params)
    except Exception:  # noqa: BLE001
        return {}


def set_query_params(**kwargs: object) -> None:
    """Update URL query parameters, ignoring failures in unsupported contexts."""
    try:
        payload = {k: v for k, v in kwargs.items() if v is not None}
        if payload:
            st.query_params.update(payload)
    except Exception:  # noqa: BLE001
        pass


def get_latest_trade_date() -> Optional[date]:
    """Fetch the most recent trade date from the database."""
    try:
        with db_session(read_only=True) as conn:
            row = conn.execute(
                "SELECT trade_date FROM daily ORDER BY trade_date DESC LIMIT 1"
            ).fetchone()
    except Exception:  # noqa: BLE001
        LOGGER.exception("查询最新交易日失败", extra=LOG_EXTRA)
        return None
    if not row:
        return None
    raw_value = row["trade_date"]
    if not raw_value:
        return None
    try:
        return datetime.strptime(str(raw_value), "%Y%m%d").date()
    except ValueError:
        try:
            return datetime.fromisoformat(str(raw_value)).date()
        except ValueError:
            LOGGER.warning("无法解析交易日：%s", raw_value, extra=LOG_EXTRA)
            return None


def default_backtest_range(window_days: int = 60) -> tuple[date, date]:
    """Return a sensible (end, start) date range for backtests."""
    latest = get_latest_trade_date() or date.today()
    start = latest - timedelta(days=window_days)
    if start > latest:
        start = latest
    return start, latest


def render_tuning_backtest_hints(current_label: Optional[str] = None) -> None:
    """Render navigation shortcuts that keep tuning and backtest flows connected."""
    key_tag = (current_label or "global").replace("/", "_")
    hint_box = st.container()
    with hint_box:
        col_go_bt, col_go_tune, col_text = st.columns([1, 1, 3])
        if col_go_bt.button("回测与复盘", key=f"hint_nav_backtest_{key_tag}"):
            navigate_top_menu("回测与复盘")
        if col_go_tune.button("实验调参", key=f"hint_nav_tuning_{key_tag}"):
            navigate_top_menu("实验调参")
        col_text.caption("提示：调参完成后记得回测验证，回测发现问题也可随时跳回调参实验。")


def estimate_llm_usage(
    *,
    start_date: date,
    end_date: date,
    universe_size: int,
    department_count: int,
    episodes: int = 1,
    prompt_tokens_per_call: int = LLM_PROMPT_TOKENS_PER_CALL,
    completion_tokens_per_call: int = LLM_COMPLETION_TOKENS_PER_CALL,
) -> Dict[str, int]:
    """Estimate aggregate LLM usage for decision-style workloads."""

    if department_count <= 0 or universe_size <= 0 or episodes <= 0:
        return {
            "days": 0,
            "decisions_per_episode": 0,
            "calls": 0,
            "prompt_tokens": 0,
            "completion_tokens": 0,
            "total_tokens": 0,
            "episodes": max(0, episodes),
        }

    delta_days = (end_date - start_date).days
    if delta_days < 0:
        return {
            "days": 0,
            "decisions_per_episode": 0,
            "calls": 0,
            "prompt_tokens": 0,
            "completion_tokens": 0,
            "total_tokens": 0,
            "episodes": max(0, episodes),
        }

    days = delta_days + 1
    if days <= 0:
        return {
            "days": 0,
            "decisions_per_episode": 0,
            "calls": 0,
            "prompt_tokens": 0,
            "completion_tokens": 0,
            "total_tokens": 0,
            "episodes": max(0, episodes),
        }

    decisions_per_episode = days * universe_size
    calls_per_episode = decisions_per_episode * department_count
    total_calls = calls_per_episode * episodes
    prompt_tokens = total_calls * max(prompt_tokens_per_call, 0)
    completion_tokens = total_calls * max(completion_tokens_per_call, 0)
    total_tokens = prompt_tokens + completion_tokens
    return {
        "days": days,
        "decisions_per_episode": decisions_per_episode,
        "calls": total_calls,
        "prompt_tokens": prompt_tokens,
        "completion_tokens": completion_tokens,
        "total_tokens": total_tokens,
        "episodes": episodes,
    }


def format_token_brief(tokens: int) -> str:
    """Return a short human-friendly token amount."""

    absolute = abs(tokens)
    if absolute >= 1_000_000:
        return f"{tokens / 1_000_000:.2f}M"
    if absolute >= 1_000:
        return f"{tokens / 1_000:.1f}K"
    return str(tokens)
